import java.util.*;
import java.util.stream.Collectors;

interface IEntity {
  Cluster getStart();

  Cluster getLocationBy(String pathName);

  void setLocations(List<Cluster> locations);

  void setPaths(Graph<String> paths);

  String reachableFrom(String currentPath);

  Subgraph kindOf(String name);

  Graph<String> getPaths();

  void processChangedFrom(Player player);
}

public class EntityFactory implements IFactory<IEntity> {
  public EntityFactory() {
  }

  @Override
  public IEntity build(String path) {
    var entity = new Entity();
    var parser = new EntityParser(path);
    parser.parse(entity);
    return entity;
  }

  private static class Entity implements IEntity {
    public List<Cluster> locations;
    public Graph<String> paths;

    Entity() {
    }

    public void setLocations(List<Cluster> locations) {
      this.locations = locations;
    }

    @Override
    public void setPaths(Graph<String> paths) {
      this.paths = paths;
    }

    @Override
    public String reachableFrom(String currentPath) {
      var edges = paths.getEdgesFrom(currentPath);
      if (!edges.isEmpty()) {
        return edges.stream().map(Graph.EdgeInfo::getTo).collect(Collectors.joining(", "));
      }
      return "";
    }

    @Override
    public Subgraph kindOf(String name) {
      for (var location : locations) {
        if (location.getName().equals(name)) return location.kind();
        var furniture = location.getFurniture().stream().filter(f -> f.getName().equals(name)).findFirst();
        if (furniture.isPresent()) return furniture.get().kind();
        var artefact = location.getArtefacts().stream().filter(a -> a.getName().equals(name)).findFirst();
        if (artefact.isPresent()) return artefact.get().kind();
        var character = location.getCharacters().stream().filter(c -> c.getName().equals(name)).findFirst();
        if (character.isPresent()) return character.get().kind();
      }
      return null;
    }

    @Override
    public Graph<String> getPaths() {
      return paths;
    }

    public List<Cluster> getLocations() {
      return locations;
    }

    @Override
    public void processChangedFrom(Player player) {
      var artefacts = player.getArtefacts();
      for (var currentLocation : getLocations()) {
        var optionalArtefactItem = artefacts.stream().filter(item -> item.validateLocation(currentLocation)).findFirst();
        if (optionalArtefactItem.isPresent()) {
          var artefactItem = optionalArtefactItem.get();
          currentLocation.mergeArtefacts(artefactItem.getArtefact());
        }
      }
    }

    @Override
    public Cluster getLocationBy(String pathName) {
      return locations.stream().filter(location -> location.getName().equals(pathName)).findFirst().orElse(null);
    }

    @Override
    public Cluster getStart() {
      if (locations == null || locations.isEmpty()) {
        return null;
      }
      return locations.get(0);
    }

  }
}
